import { Request, Response } from 'express';
import bcrypt from 'bcryptjs';
import { DatabaseConnection } from '@/database/connection';
import { AuthMiddleware } from '@/middleware/auth';
import { Logger } from '@/utils/logger';
import { 
  AuthenticationError, 
  ValidationError, 
  ConflictError,
  NotFoundError,
  asyncHandler 
} from '@/middleware/errorHandler';
import { CreateUserRequest, LoginRequest, LoginResponse } from '@/types/models';

const logger = new Logger('AuthController');

export class AuthController {
  /**
   * 用户注册
   */
  public register = asyncHandler(async (req: Request, res: Response) => {
    const { username, phone, email, password }: CreateUserRequest = req.body;
    const db = DatabaseConnection.getConnection();

    // 检查用户是否已存在
    const existingUser = await db('users')
      .where('username', username)
      .orWhere('phone', phone)
      .orWhere('email', email)
      .first();

    if (existingUser) {
      if (existingUser.username === username) {
        throw new ConflictError('用户名已存在');
      }
      if (existingUser.phone === phone) {
        throw new ConflictError('手机号已被注册');
      }
      if (existingUser.email === email) {
        throw new ConflictError('邮箱已被注册');
      }
    }

    // 密码加密
    const saltRounds = parseInt(process.env.BCRYPT_ROUNDS || '12');
    const passwordHash = await bcrypt.hash(password, saltRounds);

    // 开始事务
    const trx = await db.transaction();

    try {
      // 创建用户
      const [newUser] = await trx('users').insert({
        username,
        phone,
        email,
        password_hash: passwordHash,
        status: 'active',
        created_at: new Date(),
        updated_at: new Date()
      }).returning(['id', 'username', 'phone', 'email', 'created_at']);

      // 创建用户资料记录
      await trx('user_profiles').insert({
        user_id: newUser.id,
        timezone: 'Asia/Shanghai',
        created_at: new Date(),
        updated_at: new Date()
      });

      // 提交事务
      await trx.commit();

      logger.info('User registered successfully', {
        userId: newUser.id,
        username: newUser.username,
        phone: newUser.phone
      });

      res.status(201).json({
        success: true,
        data: {
          user: {
            id: newUser.id,
            username: newUser.username,
            phone: newUser.phone,
            email: newUser.email,
            createdAt: newUser.created_at
          }
        },
        message: '注册成功'
      });

    } catch (error) {
      await trx.rollback();
      throw error;
    }
  });

  /**
   * 用户登录
   */
  public login = asyncHandler(async (req: Request, res: Response) => {
    const { username, password }: LoginRequest = req.body;
    const db = DatabaseConnection.getConnection();

    // 查找用户
    const user = await db('users')
      .select('id', 'username', 'phone', 'email', 'password_hash', 'status')
      .where('username', username)
      .orWhere('phone', username)
      .first();

    if (!user) {
      throw new AuthenticationError('用户名或密码错误');
    }

    if (user.status !== 'active') {
      throw new AuthenticationError('账户已被禁用，请联系管理员');
    }

    // 验证密码
    const isPasswordValid = await bcrypt.compare(password, user.password_hash);
    if (!isPasswordValid) {
      throw new AuthenticationError('用户名或密码错误');
    }

    // 生成JWT令牌
    const token = AuthMiddleware.generateToken({
      id: user.id,
      username: user.username
    });

    // 生成刷新令牌
    const refreshToken = AuthMiddleware.generateRefreshToken();

    // 保存刷新令牌
    const deviceInfo = req.get('User-Agent') || 'Unknown Device';
    await AuthMiddleware.saveRefreshToken(user.id, refreshToken, deviceInfo);

    // 更新最后登录时间
    await db('users')
      .where('id', user.id)
      .update({ 
        last_login_at: new Date(),
        updated_at: new Date()
      });

    logger.info('User logged in successfully', {
      userId: user.id,
      username: user.username,
      ip: req.ip,
      userAgent: deviceInfo
    });

    const response: LoginResponse = {
      user: {
        id: user.id,
        username: user.username,
        phone: user.phone,
        email: user.email,
        status: user.status,
        last_login_at: new Date(),
        created_at: user.created_at,
        updated_at: user.updated_at
      },
      token,
      refreshToken,
      expiresIn: 7 * 24 * 60 * 60 // 7天（秒）
    };

    res.json({
      success: true,
      data: response,
      message: '登录成功'
    });
  });

  /**
   * 刷新令牌
   */
  public refreshToken = asyncHandler(async (req: Request, res: Response) => {
    const { refreshToken } = req.body;
    const { userId } = req.body;

    if (!refreshToken || !userId) {
      throw new ValidationError('缺少必要参数');
    }

    // 验证刷新令牌
    const isValid = await AuthMiddleware.verifyRefreshToken(refreshToken, userId);
    if (!isValid) {
      throw new AuthenticationError('刷新令牌无效或已过期');
    }

    const db = DatabaseConnection.getConnection();

    // 获取用户信息
    const user = await db('users')
      .select('id', 'username', 'phone', 'email', 'status')
      .where('id', userId)
      .first();

    if (!user || user.status !== 'active') {
      throw new AuthenticationError('用户不存在或已被禁用');
    }

    // 生成新的JWT令牌
    const newToken = AuthMiddleware.generateToken({
      id: user.id,
      username: user.username
    });

    // 生成新的刷新令牌
    const newRefreshToken = AuthMiddleware.generateRefreshToken();

    // 保存新的刷新令牌
    const deviceInfo = req.get('User-Agent') || 'Unknown Device';
    await AuthMiddleware.saveRefreshToken(user.id, newRefreshToken, deviceInfo);

    logger.info('Token refreshed successfully', {
      userId: user.id,
      username: user.username
    });

    res.json({
      success: true,
      data: {
        token: newToken,
        refreshToken: newRefreshToken,
        expiresIn: 7 * 24 * 60 * 60
      },
      message: '令牌刷新成功'
    });
  });

  /**
   * 用户登出
   */
  public logout = asyncHandler(async (req: Request, res: Response) => {
    const authHeader = req.headers.authorization;
    const token = authHeader?.startsWith('Bearer ') 
      ? authHeader.substring(7) 
      : authHeader;

    if (token) {
      try {
        const payload = AuthMiddleware.verifyToken(token);
        const db = DatabaseConnection.getConnection();

        // 删除用户的所有刷新令牌
        await db('refresh_tokens')
          .where('user_id', payload.userId)
          .del();

        logger.info('User logged out successfully', {
          userId: payload.userId,
          username: payload.username
        });
      } catch (error) {
        // 即使令牌验证失败，也要处理登出请求
        logger.warn('Logout with invalid token', { token });
      }
    }

    res.json({
      success: true,
      message: '登出成功'
    });
  });

  /**
   * 获取当前用户信息
   */
  public getCurrentUser = asyncHandler(async (req: Request, res: Response) => {
    const user = req.user;

    if (!user) {
      throw new AuthenticationError('用户未登录');
    }

    const db = DatabaseConnection.getConnection();

    // 获取完整的用户信息
    const fullUser = await db('users')
      .select('id', 'username', 'phone', 'email', 'avatar_url', 'status', 'last_login_at', 'created_at', 'updated_at')
      .where('id', user.id)
      .first();

    if (!fullUser) {
      throw new NotFoundError('用户不存在');
    }

    res.json({
      success: true,
      data: {
        user: fullUser
      }
    });
  });

  /**
   * 修改密码
   */
  public changePassword = asyncHandler(async (req: Request, res: Response) => {
    const { oldPassword, newPassword } = req.body;
    const user = req.user;

    if (!user) {
      throw new AuthenticationError('用户未登录');
    }

    const db = DatabaseConnection.getConnection();

    // 获取当前密码
    const currentUser = await db('users')
      .select('password_hash')
      .where('id', user.id)
      .first();

    if (!currentUser) {
      throw new NotFoundError('用户不存在');
    }

    // 验证旧密码
    const isOldPasswordValid = await bcrypt.compare(oldPassword, currentUser.password_hash);
    if (!isOldPasswordValid) {
      throw new ValidationError('当前密码错误');
    }

    // 加密新密码
    const saltRounds = parseInt(process.env.BCRYPT_ROUNDS || '12');
    const newPasswordHash = await bcrypt.hash(newPassword, saltRounds);

    // 更新密码
    await db('users')
      .where('id', user.id)
      .update({
        password_hash: newPasswordHash,
        updated_at: new Date()
      });

    // 删除所有刷新令牌（强制重新登录）
    await db('refresh_tokens')
      .where('user_id', user.id)
      .del();

    logger.info('Password changed successfully', {
      userId: user.id,
      username: user.username
    });

    res.json({
      success: true,
      message: '密码修改成功，请重新登录'
    });
  });

  /**
   * 定期清理过期令牌
   */
  public static async cleanupExpiredTokens(): Promise<void> {
    try {
      await AuthMiddleware.cleanupExpiredTokens();
    } catch (error) {
      logger.error('Failed to cleanup expired tokens:', error);
    }
  }
}